package cn.cutie.clotcache.core;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.util.stream.Stream;

/**
 * @Description:
 * @Author: Cutie
 * @CreateDate: 2024/6/13 11:53
 * @Version: 0.0.1
 */

public class ClotCacheHandler extends SimpleChannelInboundHandler<String> {
    private static final String CRLF = "\r\n";
    private static final String STR_PREFIX = "+";
    private static final String BULK_PREFIX = "$";
    private static final String OK = "OK";
    private static final String INFO = "ClotCache server[v1.0.0], created by clot." + CRLF+
                                    "Mock Redis Server at 2024-06-12 in heart." + CRLF;

    private static ClotCache cache = new ClotCache();

    /**
     * set a a100
     * *3
     * $3
     * set
     * $1
     * a
     * $4
     * a100
     *
     * get a
     * *2
     * $3
     * get
     * $1
     * a
     *
     * @param ctx           the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *                      belongs to
     * @param msg           the message to handle
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("ClotCacheHandler msg:" + msg);
        String[] args = msg.split(CRLF);
        System.out.println("ClotCacheHandler ==> " + String.join(" ", args));
        String cmd = args[2].toUpperCase();

        Command command = Commands.get(cmd);
        Reply<?> reply;
        if (command != null){
            reply = command.exec(cache, args);
            System.out.println("CMD["+ cmd +"] => " + reply.type + " => " + reply.value);
            replyContext(ctx, reply);
        } else {
            System.out.println("ClotCacheHandler ==> Unknown command: " + cmd);
            reply = Reply.error("ERR unsupported command '" + cmd + "'");
        }
        replyContext(ctx, reply);
    }

    private void replyContext(ChannelHandlerContext ctx, Reply<?> reply) {
        switch (reply.getType()){
            case INT:
                integer(ctx, (Integer) reply.getValue());
                break;
            case ERROR:
                error(ctx, (String) reply.getValue());
                break;
            case SIMPLE_STRING:
                simpleString(ctx, (String) reply.getValue());
                break;
            case BULK_STRING:
                bulkString(ctx, (String) reply.getValue());
                break;
            case ARRAY:
                array(ctx, (String[]) reply.getValue());
                break;
            default:
                simpleString(ctx, OK);
        }
    }

    private void error(ChannelHandlerContext ctx, String msg) {
        writeByteBuf(ctx, errorEncode(msg));
    }

    private String errorEncode(String msg) {
        return "-" + msg + CRLF;
    }

    private void array(ChannelHandlerContext ctx, String[] array) {
        String sb = arrayEncode(array);
        writeByteBuf(ctx, sb.toString());
    }

    private void integer(ChannelHandlerContext ctx, int i) {
        String istr = integerEncode(i);
        writeByteBuf(ctx, istr);
    }

    private void bulkString(ChannelHandlerContext ctx, String content){
        String ret = bulkStringEncode(content);
//        writeByteBuf(ctx, BULK_PREFIX + content.getBytes().length + CRLF + content + CRLF);
        writeByteBuf(ctx, ret + CRLF);
    }

    private void simpleString(ChannelHandlerContext ctx, String content){
        String ret = stringEncode(content);
        writeByteBuf(ctx, ret + CRLF);
    }

    private static String bulkStringEncode(String content) {
        String ret;
        if (content == null) {
            ret = "$-1";
        } else if (content.isEmpty()) {
            ret = "$0";
        } else {
            ret = BULK_PREFIX + content.getBytes().length + CRLF + content;
        }
        return ret + CRLF;
    }

    private static String stringEncode(String content) {
        String ret;
        if (content == null) {
            ret = "$-1";
        } else if (content.isEmpty()) {
            ret = "$0";
        } else {
            ret = STR_PREFIX + content;
        }
        return ret;
    }

    private static String integerEncode(int i) {
        String ret = ":" + i + CRLF;
        return ret;
    }

    private static String arrayEncode(Object[] array) {
        StringBuilder sb = new StringBuilder();
        if (array == null) {
            sb.append("*-1"  + CRLF);
        } else if (array.length == 0){
            sb.append("*0" + CRLF);
        } else {
            sb.append("*" + array.length + CRLF);
            for (int i = 0; i < array.length; i++) {
//                sb.append("$" + array[i].length() + CRLF + array[i] + CRLF);
                Object obj = array[i];
                if (obj == null) {
                    sb.append("$-1"  + CRLF);
                } else if (obj instanceof Integer) {
                    sb.append(integerEncode((Integer) obj));
                } else if (obj instanceof String) {
                    sb.append(bulkStringEncode((String) obj));
                } else if (obj instanceof Object[] objects) {
                    sb.append(arrayEncode(objects));
                }
            }
        }

        return sb.toString();
    }

    private void writeByteBuf(ChannelHandlerContext ctx, String content){
        System.out.println("wrap byte buffer and reply" + content);
        ByteBuf buf = Unpooled.buffer(128);
        buf.writeBytes(content.getBytes());
        ctx.writeAndFlush(buf);
    }

}

//public class ClotCacheHandler extends SimpleChannelInboundHandler<RedisMessage> {
//    @Override
//    protected void channelRead0(ChannelHandlerContext ctx, RedisMessage msg) throws Exception {
//        if (msg instanceof ArrayHeaderRedisMessage m){
//            System.out.println("1:" + m.length());
//        }
//        if (msg instanceof BulkStringHeaderRedisMessage m){
//            System.out.println("2:" +m.bulkStringLength());
//        }
//        if (msg instanceof DefaultBulkStringRedisContent m){
//            int count = m.content().readableBytes();
//            byte[] bytes = new byte[count];
//            m.content().readBytes(bytes);
//            System.out.println("3:" + new String(bytes));
//            ctx.writeAndFlush("+OK\r\n");
//        }
//    }
